package fr.cephb.joperon.tool.stypesgui;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.xml.sax.InputSource;


import com.sleepycat.db.DatabaseException;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

import fr.cephb.net.HttpApplication;

/**
 * STypesServer
 * @author lindenb
 *
 */
public class STypesServer extends HttpApplication
{
private static final Logger LOG=Logger.getLogger("fr.cephb.joperon");
/** path */
private String path="/stypes";
/** server */
private HttpServer server=null;	
/** config File */
private File configFile=null;
/** secured server */
private String password=null;


public void setServer(HttpServer server) {
	this.server = server;
	}

private void callVelocity(
		String templateName,
		VelocityContext context,
		PrintWriter out
		)
throws IOException
	{
	LOG.info("calling velocity template "+templateName);
	VelocityEngine engine = new VelocityEngine();
	
	if("a".equals("b"))
		{
		engine.setProperty(VelocityEngine.RESOURCE_LOADER, "file");
		engine.setProperty(
	            "class." + VelocityEngine.RESOURCE_LOADER + ".class",
	            ClasspathResourceLoader.class.getName()
	            );
		engine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH,
				"/home/pierre/polymorphism/joperon/src/java/fr/cephb/joperon/tool/stypesgui"
				);
		}
	else
		{
		engine.setProperty(VelocityEngine.RESOURCE_LOADER, "class");
		engine.setProperty(
	            "class." + VelocityEngine.RESOURCE_LOADER + ".class",
	            ClasspathResourceLoader.class.getName()
	            );
		}
	try
		{
		engine.init();
		Template template =   engine.getTemplate(templateName);
		template.merge(context, out);
		out.flush();
		}
	catch(Exception e)
		{
		LOG.throwing(getClass().getName(), "callVelocity", e);
		e.printStackTrace(out);
		}
	}




@Override
public void handle(HttpExchange exchange) throws IOException
	{
	LOG.info("handling exchange");
	Properties params=getParametersAsMap(exchange);
    
	if(params.getProperty("admin", "").equals("shutdown") || this.server==null)
		{
		LOG.info("shutdown");
		Headers responseHeaders = exchange.getResponseHeaders();
		responseHeaders.set("Content-Type", "text/plain");
		exchange.sendResponseHeaders(200, 0);
		PrintWriter out = new PrintWriter(exchange.getResponseBody());
		out.println("server stopped.");
		out.flush();
		out.close();
		if(this.server!=null)
			{
			HttpServer old= this.server;
			this.server=null;
			old.stop(60);//wait one minute
			}
		return;
		}
	else if(params.getProperty("action", "").equals("run"))
		{
		LOG.info("run");
		//remove empty contents
		for(Iterator<Map.Entry<Object,Object>> iter=params.entrySet().iterator();
			iter.hasNext();)
			{
			Map.Entry<Object,Object> e=iter.next();
			if(e.getValue().toString().trim().isEmpty())
				{
				iter.remove();
				}
			}
		PrintStream out=null;
		Model model=null;
		try
			{
			Integer chrom_id=null;
			Integer view_start=null;
			Integer view_end=null;
			String script=params.getProperty("script");
			Scanner.Mode mode=Scanner.Mode.text;
			try {
				if(this.password!=null &&
				   !this.password.equals(params.getProperty("password")))
					{
					throw new IllegalArgumentException("password missing/wrong");
					}
				
				
				
				if(params.containsKey("chrom"))
					{
					chrom_id= Integer.parseInt(params.getProperty("chrom"));
					}
				if(params.containsKey("start"))
					{
					view_start= Integer.parseInt(params.getProperty("start"));
					}
				if(params.containsKey("end"))
					{
					view_end= Integer.parseInt(params.getProperty("end"));
					}
					if(params.containsKey("mode"))
					{
					mode= Scanner.Mode.valueOf(params.getProperty("mode"));
					}
				if(chrom_id!=null && (chrom_id<1 || chrom_id>99))
					{
					throw new IllegalArgumentException("bad chrom number");
					}
				if(view_start!=null && view_start<1)
					{
					throw new IllegalArgumentException("bad start index");
					}
				if(view_end!=null && view_end<1)
					{
					throw new IllegalArgumentException("bad end index");
					}
				
				if(script==null)
					{
					throw new IllegalArgumentException("Script missing");
					}
				
				model=new Model();
				model.open(this.configFile);
				
				
				
				Scanner scanner= new Scanner();
				scanner.setModel(model);
				scanner.setScreenSize(
						Integer.parseInt(params.getProperty("width","0"))
						, Integer.parseInt(params.getProperty("height","0"))
						);
				scanner.addQueries(new InputSource(new StringReader(script)));
				Scanner.MatchHandler handler=scanner.newHandler(mode);
				
				handler.setBounds(chrom_id,view_start,view_end);
				
				Headers responseHeaders = exchange.getResponseHeaders();
				String mime= "text/plain";
				switch(mode)
					{
					case text: mime= "text/plain"; break;
					case xml: mime= "text/xml"; break;
					case json:mime="application/json"; break;
					case svg:mime="image/svg+xml"; break;
					default:throw new UnsupportedOperationException("Not implemented "+mode);
					}
				responseHeaders.set("Content-Type", mime);
				if(params.getProperty("attach", "true").equals("true"))
					{
					responseHeaders.set("Content-Disposition","attachment; filename=\"stypes."+mode+"\"");
					}
				exchange.sendResponseHeaders(200, 0);
				
				out = new PrintStream(exchange.getResponseBody());
				handler.setWriter(out);
				
				LOG.info("startDocument");
				handler.startDocument();
				scanner.scan(handler);
				handler.endDocument();
				LOG.info("endDocument");
				}
			catch (Throwable e)
				{
				LOG.throwing(getClass().getName(), "handle", e);
				Headers responseHeaders = exchange.getResponseHeaders();
				responseHeaders.set("Content-Type", "text/plain");
				exchange.sendResponseHeaders(200, 0);
				out = new PrintStream(exchange.getResponseBody());
				out.println("An error occured:"+e.getMessage());
				out.println("\n\ntrace:\n");
				e.printStackTrace(out);
				return;
				}
			}
		catch(Throwable e2)
			{
			throw new IOException(e2);
			}
		finally
			{
			
			/* close out */
			if(out!=null)
				{
				out.flush();
				out.close();
				}
	
			/* close model */
			if(model!=null)
				{
				try {
					model.close();
					} 
				catch (DatabaseException e)
					{
					LOG.throwing(getClass().getName(), "run", e);
					}
				}
			}
		}
	else
		{
		LOG.info("printing home page");
		Headers responseHeaders = exchange.getResponseHeaders();
		responseHeaders.set("Content-Type", "application/xhtml+xml");
		exchange.sendResponseHeaders(200, 0);
		PrintWriter out = new PrintWriter(exchange.getResponseBody());
		VelocityContext ctx= new VelocityContext();
		ctx.put("path", this.path);
		ctx.put("start", params.getProperty("start",""));
		ctx.put("end", params.getProperty("end",""));
		ctx.put("chrom", params.getProperty("chrom",""));
		ctx.put("script", params.getProperty("script",""));
		ctx.put("modes", Scanner.Mode.values());
		if(this.password!=null) ctx.put("password","");
		callVelocity("/META-INF/form.html.vm",ctx,out);
		out.flush();
		out.close();
		return;
		}
	 //never reached
	 
	
	
 
	}
	
		
    
/**
 * main
 */
public static void main(String[] args)
	{
	LOG.setLevel(Level.OFF);
	
	
	try {
		 STypesServer test=new STypesServer();
		
		 int optind=0;
	
		 while(optind<args.length)
				{
				if(args[optind].equals("-h"))
					{
					System.err.println("Pierre Lindenbaum PhD.");
					System.err.println(" -log-level <java.util.logging.Level>");
					System.err.println("-h this screen");
					System.err.println("-password <s> a password wil be required to run the server");
					System.err.println("-p <port> default:"+test.port);
					System.err.println("-P <path> default:"+test.path);
					System.err.println("-cfg <config-file>");
					return;
					}
				else if(args[optind].equals("-log-level"))
					{
					LOG.setLevel(Level.parse(args[++optind]));
					}
				else if(args[optind].equals("-p"))
					{
					test.port =Integer.parseInt(args[++optind]);
					}
				else if(args[optind].equals("-P"))
					{
					test.path=args[++optind];
					}
				else if(args[optind].equals("-cfg"))
					{
					test.configFile=new File(args[++optind]);
					}
				else if(args[optind].equals("-password"))
					{
					test.password= args[++optind];
					}
				 else if (args[optind].equals("--"))
				     {
				     ++optind;
				     break;
				     }
				else if (args[optind].startsWith("-"))
				     {
				     System.err.println("bad argument " + args[optind]);
				     System.exit(-1);
				     }
				else
				     {
				     break;
				     }
				++optind;
				}
		 
		 if(test.configFile==null)
		 	{
			System.err.println("Undefined config file");
			return;
		 	}
		 
		 if(!test.configFile.exists())
		 	{
			System.err.println("Config file does not exists : "+test.configFile);
			return;
		 	}
		 
		 HttpServer server = HttpServer.create(new InetSocketAddress(test.getPort()), 0);
		 test.setServer(server);
		 LOG.info("creating context");
		 server.createContext(test.path, test);
		 LOG.info("starting server http://localhost:"+test.port+test.path);
		 server.start();
		 
		} 
	catch (IOException e)
		{
		e.printStackTrace();
		}

	}
}
